#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import os.path
import sys
import shutil
import sqlite3
from io import StringIO
import json
import time
import datetime
import requests
from git import Repo
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from transliterate import translit
from pathlib import Path
import zipfile
import glob
import collections
import importlib
import cgi
import uuid
import tempfile
import hashlib


local_path = os.path.split(__file__)[0]
if local_path not in sys.path:
    sys.path.insert(0, local_path)

import mzrepos_prefs as prefs

sys.path.insert(0, prefs.TDS_PREFS_PATH)
tds_prefs = importlib.import_module("tds_prefs")

tds_templates = {}  # список типов РЭМД, которые поддержаны в ШМД, опубликованные в сервисе распространения
fnsi_emd = {}       # все справочники ФНСИ, привязанные к типам РЭМД      
fnsi_emd2 = {}       # хеш: тип Рэмд - название типа
fnsi_emd_type_active = {} # хеш: тип МД - последний тип РЭМД

block_size = 4096


# Вычисление хеша файла
def filehash(fn) -> str:
    md5 = hashlib.md5()
    with open(fn, 'rb') as f:
        while True:
            data = f.read(65536)
            if not data:
                break
            md5.update(data)
    return "{0}".format(md5.hexdigest())


# Утилита для формирование in-memory БД.
# В SQLPacket первым столбцом всегда должен быть uuid или другой строковый идентификатор
def Execute(hash, SQLPacket, type, connection) :
    try :
        cursor = connection.cursor()
        cursor.execute(SQLPacket)
        columns = []
        for row in cursor.description :
            columns.append(row[0])
        for objReader in cursor.fetchall() :
            id = objReader[0]
            if type == "HASH" :  # ключ - значение
                hash[id] = objReader[1]
            elif type == "HASH_HASH" :  # ключ - одна запись (hash)
                v = {}
                for column in range(len(objReader)) :
                    v[columns[column]] = objReader[column]
                    
                hash[id] = v
            elif type == "HASH_ARRAY_STRING_QOUTES" :  # ключ - массив строк в кавычках
                v = "\"" +objReader[1] + "\""
                if id not in hash :
                    hash[id] = [v]
                else :
                    hash[id].append(v)
            elif type == "HASH_ARRAY_STRING" :  # ключ - массив строк без кавычек
                v = objReader[1]
                if id not in hash :
                    hash[id] = [v]
                else :
                    hash[id].append(v)
            elif type == "HASH_ARRAY_HASH" :  # ключ - массив записей (хешей)
                v = {}
                for column in range(len(objReader)) :
                    v[columns[column]] = objReader[column]
                    
                if id not in hash :
                    hash[id] = [v]
                else :
                    hash[id].append(v)
            else :
                raise Exception("Неизвестное значение параметра type")
                
        cursor.close()
    except Exception as ex :
        print(SQLPacket)
        raise  


def read(environ, length):
    stream = environ['wsgi.input']
    body = tempfile.TemporaryFile(mode='w+b')
    while length > 0:
        part = stream.read(min(length, 1024*200)) # 200KB buffer size
        if not part: break
        body.write(part)
        length -= len(part)
    body.seek(0)
    environ['wsgi.input'] = body
    return body


def loadFNSIref_semd_spec(conn, errors_file) -> str:
    if prefs.FNSI_userkey is None or len(prefs.FNSI_userkey) == 0 :
        return

    cursor = conn.cursor()
    cursor.execute("select version from fnsi_ref_ver where oid='1.2.643.5.1.13.13.99.2.638'")
    ret = cursor.fetchone()
    cursor.close()

    r = requests.get('https://nsi.rosminzdrav.ru:443/port/rest/passport',
        params = {'userKey': prefs.FNSI_userkey, 'identifier': '1.2.643.5.1.13.13.99.2.638'},
        headers = {'Accept': 'application/json'}
    )
    if r.status_code != 200:
        raise Exception('FNSI response code = ' + str(r.status_code) + " " + r.text)

    try:
        passport = r.json()
    except Exception as e:
        print(r, sep=' ', end='', file=environ["wsgi.errors"])
        raise

    if ret[0] == passport['version'] :
        return "Справочник спецификаций актуальный"

    cursor = conn.cursor()
    cursor.execute("delete from fnsi_semd_spec")
    cursor.close()

    params = {"OID":0, "FULL_NAME":1, "RELEASE":2, "GIT_PUB_DATE":3, "GIT_LINK":4}

    p = ','.join(['?'] * len(params))
    pa = [None]*len(params)
    SQLPacket = "INSERT INTO fnsi_semd_spec VALUES (" + p + ")"

    c = int((passport["rowsCount"]-1)/prefs.FNSI_PORTION)+1
    for page in range(1, c+1) :
        r = requests.get('https://nsi.rosminzdrav.ru:443/port/rest/data',
            params = {'userKey': prefs.FNSI_userkey, 'identifier': '1.2.643.5.1.13.13.99.2.638', 'page': page, 'size':prefs.FNSI_PORTION},
            headers = {'Accept': 'application/json'}
        )
        if r.status_code != 200:
            raise Exception('FNSI response code = ' + str(r.status_code) + " " + r.text)
        try :
            data = r.json()
        except Exception as e:
            print(r, sep=' ', end='', file=errors_file)
            raise

        for psObject in data["list"] :
            for i in range(len(pa)) :
                pa[i] = None

            for obj in psObject :
                obj["column"] = obj["column"].upper()
                try :
                    i1 = params[obj["column"]]
                    if obj["value"] != None :
                        pa[i1] = obj["value"]
                except KeyError as ex:
                    pass

            try :
                cursor = conn.cursor()
                cursor.execute(SQLPacket, pa)
            except Exception as ex:
                print(pa, ex, sep='\n', end='', file=errors_file)

    cursor = conn.cursor()
    cursor.execute("update fnsi_ref_ver set version=? where oid='1.2.643.5.1.13.13.99.2.638'", (passport['version'],))
    cursor.close()

    conn.commit()
    return "Справочник спецификаций обновлен до версии "+passport['version']


# определение наличия изменений в 1.2.643.5.1.13.13.11.1520. 
# В случае наличия изменений выполняется отправка уведомительного письма
def loadFNSIref_emd_notification(conn, errors_file) -> str :
    if prefs.FNSI_userkey is None or len(prefs.FNSI_userkey) == 0 :
        return

    cursor = conn.cursor()
    cursor.execute("select version from fnsi_ref_ver where oid='fnsi_emd_notification'")
    ret = cursor.fetchone()
    cursor.close()

    r = requests.get('https://nsi.rosminzdrav.ru:443/port/rest/passport',
        params = {'userKey': prefs.FNSI_userkey, 'identifier': '1.2.643.5.1.13.13.11.1520'},
        headers = {'Accept': 'application/json'}
    )
    if r.status_code != 200:
        raise Exception('FNSI response code = ' + str(r.status_code) + " " + r.text)

    try:
        passport = r.json()
    except Exception as e:
        print(r, sep=' ', end='', file=environ["wsgi.errors"])
        raise

    if ret[0] == passport['version'] :
        return "Справочник типов РЭМД актуальный"

    cursor = conn.cursor()
    prev = {}
    cursor.execute("select ID, END_DATE, IMPLEMENTATION_GUIDE from fnsi_emd_notification")
    for r in cursor.fetchall():
        prev[str(r[0])] = (r[1], r[2])
    cursor.close()

    cursor = conn.cursor()
    cursor.execute("delete from fnsi_emd_notification")
    cursor.close()

    params = {"ID":0, "TYPE":1, "NAME":2, "START_DATE":3, "END_DATE":4, "IMPLEMENTATION_GUIDE":5, "GIT_LINK":6}

    p = ','.join(['?'] * len(params))
    pa = [None]*len(params)
    SQLPacket = "INSERT INTO fnsi_emd_notification VALUES (" + p + ")"

    c = int((passport["rowsCount"]-1)/prefs.FNSI_PORTION)+1
    for page in range(1, c+1) :
        r = requests.get('https://nsi.rosminzdrav.ru:443/port/rest/data',
            params = {'userKey': prefs.FNSI_userkey, 'identifier': '1.2.643.5.1.13.13.11.1520', 'page': page, 'size':prefs.FNSI_PORTION},
            headers = {'Accept': 'application/json'}
        )
        if r.status_code != 200:
            raise Exception('FNSI response code = ' + str(r.status_code) + " " + r.text)
        try :
            data = r.json()
        except Exception as e:
            print(r, sep=' ', end='', file=errors_file)
            raise

        for psObject in data["list"] :
            for i in range(len(pa)) :
                pa[i] = None

            ID = None
            NAME = None
            END_DATE = None
            IMPLEMENTATION_GUIDE = None
            for obj in psObject :
                obj["column"] = obj["column"].upper()
                if obj["column"] == "ID":
                    ID = obj["value"]
                elif obj["column"] == "NAME":
                    NAME = obj["value"]
                elif obj["column"] == "END_DATE":
                    END_DATE = obj["value"]
                elif obj["column"] == "IMPLEMENTATION_GUIDE":
                    IMPLEMENTATION_GUIDE = obj["value"]
                try :
                    i1 = params[obj["column"]]
                    if obj["value"] != None :
                        pa[i1] = obj["value"]
                except KeyError as ex:
                    pass

            if len(prev) > 0:   # при первоначальной загрузке сообщения не отсылаем. 
                if ID not in prev:
                    send_mail('СЭМДы. Новый тип РЭМД - '+ID, 
                        'В справочник 1.2.643.5.1.13.13.11.1520 добавлен новый тип РЭМД - '+ID+' "'+NAME+'".\n\nОткрыть - '+prefs.SITE_DOMAIN+prefs.SITE_URL+'/repo/'+ID)
                elif prev[ID][0] != END_DATE:
                    if int(ID) in prefs.TYPE_REMD_IN_CONFIG or ID in tds_templates:
                        send_mail('СЭМДы. У типа РЭМД '+ID+' изменилась дата окончания регистрации', 
                            'У типа РЭМД '+ID+' "'+NAME+'" изменилась дата окончания регистрации. Было - '+str(prev[ID][0])+'. Стало '+str(END_DATE)+'.\n\nОткрыть - '+prefs.SITE_DOMAIN+prefs.SITE_URL+'/repo/'+ID)
                elif prev[ID][1] is None and IMPLEMENTATION_GUIDE is not None:
                    send_mail('СЭМДы. У типа РЭМД '+ID+' опубликовано руководство по реализации', 
                        'У типа РЭМД '+ID+' "'+NAME+'" опубликовано руководство по реализации.\n\nОткрыть - '+prefs.SITE_DOMAIN+prefs.SITE_URL+'/repo/'+ID)

            try :
                cursor = conn.cursor()
                cursor.execute(SQLPacket, pa)
            except Exception as ex:
                print(pa, ex, sep='\n', end='', file=errors_file)

    cursor = conn.cursor()
    cursor.execute("update fnsi_ref_ver set version=? where oid='fnsi_emd_notification'", (passport['version'],))
    cursor.close()

    conn.commit()
    return "Справочник типов РЭМД обновлен до версии "+passport['version']


def escapeHTML(line: str) -> str :
    if line is None:
        return ""
    return line.strip().replace(">", "&#62;").replace("<", "&#60;").replace("\"", "&#34;").replace('\n', "<br>").replace('\t', "&#9;").replace("'", "&apos;").replace('\\', "&#92;")


def slug(url : str) -> str:
    return translit(url, language_code='ru', reversed=True).replace(' ', '_').replace('\'', '_')


def send_mail(header: str, body: str):
    try:
        s = None
        s = smtplib.SMTP(prefs.SMTP_HOST, prefs.SMTP_PORT, timeout=10)
        #s.set_debuglevel(1)
        #s.starttls()
        if len(prefs.SMTP_LOGIN) > 0:
            s.login(prefs.SMTP_LOGIN, prefs.SMTP_PASSWORD)

        output = StringIO()
        print(body, sep='', end='', file=output)

        msg = MIMEText(output.getvalue(), 'plain', 'utf-8')
        msg['Subject'] = Header(header, 'utf-8')
        msg['From'] = prefs.SMTP_FROM
        msg['To'] = ", ".join(prefs.RECIPIENTS)
        s.sendmail(msg['From'], prefs.RECIPIENTS, msg.as_string())
    finally:
        if s is not None:
            s.quit()


def compareFiles(file1 : Path, file2 : Path) -> bool:
    f1 = open(file1.absolute(), "r")  
    f2 = open(file1.absolute(), "r")  
    while f1:
        line1 = f1.readline()
        line2 = f2.readline()
        if line1 != line2:
            f1.close()                                       
            f2.close()
            return False
        if line1 == "":
            f1.close()                                       
            f2.close()
            return True


# создание или обновление репозитория. Важно создавать и обновлять репозитории в порядке - сначала старые редакции, потом новые.
def updaterepo_func(conn, type_remd: str) -> bool:
    cur = conn.cursor()
    cur.execute('''select fnsi_emd.name, fnsi_semd_spec.GIT_LINK 
        from fnsi_emd 
        inner join fnsi_semd_spec on fnsi_emd.git_link=fnsi_semd_spec.OID 
        where id=?''', (int(type_remd),))
    rec = cur.fetchone()
    cur.close()

    if rec is None:
        return False

    repopath_a = rec[1].split('/')
    branch_path = repopath_a[7]
    repopath = os.path.join(prefs.DATA_PATH, type_remd)
    active_tag = None
    if not os.path.exists(repopath):
        os.makedirs(repopath, exist_ok=True)
        Repo.clone_from(prefs.REPO_URL+repopath_a[4], repopath, branch=branch_path)
        repo = Repo(repopath)

        branch = {}
        for c in repo.iter_commits(first_parent=True):
            branch[c] = c
        found = False
        for t in repo.tags:
            c = repo.commit(t)
            if c in branch:
                found = True
                commit_time = time.gmtime(c.committed_date)
                cur = conn.cursor()
                cur.execute("insert into semd_spec_repo values (?,?,?,?,?)", (str(c), int(type_remd), str(t), time.strftime("%d.%m.%Y", commit_time), c.message))
                cur.close()

    else :
        repo = Repo(repopath)
        repo.git.checkout(branch_path)
        repo.remotes.origin.pull()
        prev = {}
        cur = conn.cursor()
        cur.execute("select active_tag from repo_active_tag where type_remd=? and last_tag<>active_tag", (int(type_remd),))
        r = cur.fetchone()
        if r is not None:
            active_tag = r[0]
        cur.close()
        cur = conn.cursor()
        cur.execute("select repo_commit from semd_spec_repo where type_remd=?", (int(type_remd),))
        for r in cur.fetchall():
            prev[r[0]] = r[0]
        cur.close()

        branch = {}
        for c in repo.iter_commits(first_parent=True):
            branch[c] = c
        for t in repo.tags:
            c = repo.commit(t)
            if str(c) not in prev and c in branch:
                commit_time = time.gmtime(c.committed_date)
                cur = conn.cursor()
                cur.execute("insert into semd_spec_repo values (?,?,?,?,?)", (str(c), int(type_remd), str(t), time.strftime("%d.%m.%Y", commit_time), c.message))
                cur.close()
                if int(type_remd) in prefs.TYPE_REMD_IN_CONFIG or type_remd in tds_templates:
                    send_mail('СЭМДы. В репозитории типа РЭМД '+ type_remd+' установлен новый тег',
                        'В репозитории типа РЭМД '+ type_remd+' "'+rec[0]+'" установлен новый тег -"'+str(t)+'".'+('\n\nКомментарий -"'+c.message.strip()+'".' if c.message is not None else '')+'\n\nОткрыть - '+prefs.SITE_DOMAIN+prefs.SITE_URL+'/repo/'+type_remd)

        cur = conn.cursor()
        cur.execute("delete from repo_active_tag where type_remd=?", (int(type_remd),))
        cur.close()

        try:
            os.remove(os.path.join(repopath, type_remd+'.zip'))
        except OSError as e:
            pass

    cur = conn.cursor()
    SQLPacket = '''select tag, repo_commit
          from semd_spec_repo
          where type_remd=?
          order by substr(commit_date, 7)||substr(commit_date, 4, 2)||substr(commit_date, 1, 2) desc limit 1'''  # dd.mm.yyyy -> yyyymmdd. Берем первую запись - самый свежий тег
    cur.execute(SQLPacket, (int(type_remd),))

    r = cur.fetchone()
    if r is not None:
        if active_tag is None:
            active_tag = r[0]
        repo.git.checkout(active_tag)
        cur = conn.cursor()
        cur.execute("insert into repo_active_tag values (?,?,?,?)", (int(type_remd), active_tag, r[0], None))
        cur.close()

    conn.commit()
    return True


CONFIG_NAMES = []
for name in tds_prefs.CONFIGS:
    CONFIG_NAMES.append(name)
CONFIG_NAMES.sort()

CONFIG_VERSIONS = []
for name in tds_prefs.CONFIGS:
    CONFIG_VERSIONS = list(set().union(CONFIG_VERSIONS, tds_prefs.CONFIGS[name]))
CONFIG_VERSIONS.sort()

CONFIG_VERSIONS_IDX = {}
for name in CONFIG_NAMES:
    vers = tds_prefs.CONFIGS[name]
    for i in range(0, len(vers)):
        CONFIG_VERSIONS_IDX[name+"_"+vers[i]] = i

conf = next(iter(tds_prefs.CONFIGS))        # получаем первый элемент словаря
cv = tds_prefs.CONFIGS[conf][-1]

SQLPacket = f'''select typeREMDCode, template.fileName
      from template 
      inner join templateType on template.UUIDTemplate=templateType.UUIDTemplate
      where configName='{conf}' and configVersion='{cv}' '''

conn = sqlite3.connect(os.path.join(tds_prefs.DATA_PATH, tds_prefs.DB_NAME))
Execute(tds_templates, SQLPacket, "HASH", conn)
conn.close()


conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
Execute(fnsi_emd2, 'select cast(id as text), name from fnsi_emd', "HASH", conn)
Execute(fnsi_emd_type_active, "select type, max(id) from fnsi_emd group by type", "HASH", conn)
conn.close()


def get_fnsi_emd(conn) :
    fnsi_emd.clear()
    SQLPacket = '''select ref||' '||fnsi_ref.version r, fnsi_emd.id, fnsi_emd.name, end_date
    from fnsi_semd_refs
    inner join fnsi_emd on fnsi_emd.git_link=fnsi_semd_refs.oid
    inner join fnsi_ref on fnsi_semd_refs.ref=fnsi_ref.codeSystem and fnsi_semd_refs.version=fnsi_ref.actual'''
    Execute(fnsi_emd, SQLPacket, "HASH_ARRAY_HASH", conn)

conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
get_fnsi_emd(conn)
conn.close()


def application(environ, start_response):
    if environ['PATH_INFO'] == '/style.css':
        style=b'''
table.bordered {
    display: table;
    border-collapse: separate;
    box-sizing: border-box;
    white-space: normal;
    line-height: normal;
    font-weight: normal;
    font-size: small;
    font-style: normal;
    color: -internal-quirk-inherit;
    text-align: start;
    border: 1px outset;
    border-spacing: 0px;
    border-color: grey;
    font-variant: normal;
    font-family: Verdana, Tahoma, Arial, sans-serif;
}
table {
    line-height: normal;
    font-weight: normal;
    font-size: small;
    font-style: normal;
    font-variant: normal;
    font-family: Verdana, Tahoma, Arial, sans-serif;
}
th, td {
   padding: 3px 3px;
}
.selected tr:hover td {
    background-color: #9090F0;
}
p  {
    font-family: Verdana, Tahoma, Arial, sans-serif;
    contain: content;
}
.deleted {
    background-color: rgb(255,150,150) !important;
}
.added {
    background-color: rgb(150,255,150) !important;
}
'''
        start_response('200 OK', [
            ('Content-Type', 'text/css; charset=utf-8'),
            ('Content-Length', str(len(style)))
        ])
        return [style]


    if environ['PATH_INFO'] == '/index.html' or environ['PATH_INFO'] == '/':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        cur.execute("select version from fnsi_ref_ver where oid = 'repos'")
        actuality = cur.fetchone()[0]
        cur.close()
        output = StringIO()

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Спецификации СЭМД</title>
</head>
<body>
<H1>Спецификации СЭМД</H1>
<p>Дата актуальности - ''',actuality,'''</p>
<p><a href="''', prefs.SITE_URL, '''/s/send_semd.html">Валидатор СЭМД</a></p>
<p><a href="''', prefs.TDS_SITE_URL, '''/getFullTemplatesList">Сервис распространения ШМД</a></p>
<p><a href="''', prefs.SITE_URL, '''/s/download_all">Скачать всё (без руководств)</a></p>
<p><a href="''', prefs.SITE_URL, '''/s/clear_active" target="_blank">Сбросить откаты по тегам (активировать последние теги)</a></p>
<p><a href="''', prefs.SITE_URL, '''/s/check_validator_sch">Проверить базу SCH валидатора</a></p>
<p><a href="''', prefs.SITE_URL, '''/s/check_validator_xsd">Проверить базу XSD валидатора</a></p>
<p><a href="''', prefs.SITE_URL, '''/s/check_tds_vis">Проверить XSL в сервисе распространения ШМД</a></p>
<p><a href="''', prefs.SITE_URL, '''/refs">Справочники ФНСИ</a></p>
<table class="selected bordered" style='width: 100%; table-layout : fixed;' border=1>
<tr><th width="3%">Тип РЭМД</th><th>Имя</th><th width="20%">Портал</th><th width="5%">Начало регистрации</th><th width="5%">Окончание регистрации</th></tr>''', sep='', end='\n', file=output);

        cur = conn.cursor()
        SQLPacket = '''select fnsi_emd.id, fnsi_emd.name, IMPLEMENTATION_GUIDE, START_DATE, END_DATE, fnsi_semd_spec.GIT_LINK, repo_active_tag.active_tag, repo_active_tag.user, fnsi_emd.type
            from fnsi_semd_spec
            inner join fnsi_emd on fnsi_emd.git_link=fnsi_semd_spec.OID
            left join repo_active_tag on repo_active_tag.type_remd=fnsi_emd.id and active_tag<>last_tag
            order by fnsi_emd.id'''
        cur.execute(SQLPacket)
        count = 0
        count2 = 0
        done = 0
        done2 = 0
        emd_types = set()
        emd_done_types = set()
        for r in cur.fetchall():
            repopath = r[5][r[5].rfind('/')+1:]
            type_remd = f"<span title='Тип МД - {r[8]}'>"+str(r[0])+"</span>"
            repo = "<a href='" + prefs.SITE_URL + '/repo/' + str(r[0]) + "'>" + r[1] + "</a>"
            repo_active_tag = '<br><small><strong>'+r[6]+'</strong>'+ (' Пользователь - '+r[7] if r[7] is not None else '')+'</small>' if r[6] is not None else ''
            delta_end = datetime.datetime.now()-datetime.datetime.strptime(r[4], '%d.%m.%Y') if r[4] is not None else datetime.timedelta(days = -1000)
            tr_repo_color = ""
            td1_active = ""
            td1_tds_color = ""
            td2_repo_color = ""
            td5_repo_color = ""
            td1_tds = ""
            if delta_end.days > 0:
                tr_repo_color = " bgcolor='#cccccc'"
            elif delta_end.days > -30:
                td5_repo_color = " bgcolor='#ffff11'"
            elif delta_end.days > -90:
                td5_repo_color = " bgcolor='#ffcc55'"
            if r[6] is not None:
                td2_repo_color = " bgcolor='#FF5050'"
            if str(r[0]) in tds_templates:
                td1_tds_color = " bgcolor='#A0FFFF'"
                td1_tds = " <span title='Реализовано в ШМД'>Ш</span>"
                done += 1
            elif r[0] in prefs.TYPE_REMD_IN_CONFIG:
                td1_tds_color = " bgcolor='#A0FFFF'"
                td1_tds = " <span title='Реализовано в конфигурации'>К</span>"
                done += 1
            if fnsi_emd_type_active[r[8]] == r[0]:
                td1_active += "<span style='font-weight:bold;font-size:x-small' title='Актуальная редакция СЭМД'>(A)</span>"
            print('<tr',tr_repo_color,'><td align="center"',td1_tds_color,'>',type_remd,td1_active,td1_tds,'</td>',
                '<td',td2_repo_color,'>',repo,repo_active_tag,'</td>',
                '<td>',"<a href='"+r[2]+"'>"+r[2]+"</a>" if r[2] is not None else "",'</td>',
                '<td>',r[3],'</td>',
                '<td',td5_repo_color,'>',r[4] if r[4] is not None else "",'</td></tr>', sep='', end='\n', file=output)
            count += 1
            if r[2] is not None and delta_end.days<=0:
                count2 += 1
                emd_types.add(repopath)
                if td1_tds_color != "":
                    emd_done_types.add(repopath)

        cur.close()
        conn.close()
        print('</table>', sep='', end='', file=output)
        print('<p>Количество типов РЭМД - ', count, '<br>', sep='', end='', file=output)
        print('&nbsp;Из них активных - ', count2, '. Поддержано - ', done, '<br>', sep='', end='', file=output)
        print('&nbsp;&nbsp;Из них типов ЭМД - ', len(emd_types), '. Поддержано - ', len(emd_done_types),  '</p>', sep='', end='', file=output)
        print('</body></html>', sep='', end='', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]

    url = environ['PATH_INFO'].split('/')
    secret = True if len(url) > 1 and url[1] == 's' else False
    if secret:          # все нижележащие url могут находится в зоне с ограниченным доступом, префикс может быть равен 's'
        s = url.pop(0)

    if len(url) >= 3 and url[1] == 'repo' and url[2].isnumeric() and "\\.." not in environ['PATH_INFO']:
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select fnsi_emd.name, IMPLEMENTATION_GUIDE, fnsi_semd_spec.GIT_LINK, fnsi_emd.GIT_LINK 
            from fnsi_emd 
            inner join fnsi_semd_spec on fnsi_semd_spec.oid=fnsi_emd.GIT_LINK 
            where id=?'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()

        if rec is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']


        output = StringIO()
        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Репозиторий Минздрава РФ</title>
</head>
<body>
<H2>''',rec[0],' (тип РЭМД - ',url[2],''')</H2>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p><p>\n''',
'Портал - <a href="'+rec[1]+'">'+ rec[1]+'</a><br>' if rec[1] is not None else "",
'Репозиторий Минздрава - <a href="'+rec[2]+'">', rec[2],'''</a></p>
<table class='bordered' style='width: 100%; table-layout : fixed;'>
<tr><th width="5%">Дата</th><th>Тег</th><th width="45%">Комментарий</th></tr>''', sep='', end='\n', file=output);
        branch = rec[3]

        found = False
        cur = conn.cursor()
        SQLPacket = '''select commit_date, tag, repo_commit, comment, active_tag, last_tag, repo_active_tag.user
            from semd_spec_repo
            left join repo_active_tag on repo_active_tag.type_remd=semd_spec_repo.type_remd
            where semd_spec_repo.type_remd=?
            order by substr(commit_date, 7)||substr(commit_date, 4, 2)||substr(commit_date, 1, 2) desc'''  # dd.mm.yyyy -> yyyymmdd
        cur.execute(SQLPacket, (int(url[2]),))
        for r in cur.fetchall():
            found = True
            print('<tr><td>',r[0],'</td>', file=output)
            if r[1] != r[4] and r[4] is not None:
                print('<td><a href="'+prefs.SITE_URL+'/s/checkout/'+url[2]+'/'+r[1]+'"  target="_blank">'+r[1]+'</a></td>', file=output)
            else:
                print('<td style="background:'+('#50FF50' if r[4]==r[5] else '#FF5050')+'"><strong>',r[1],'</strong>',(' Пользователь - '+r[6] if r[6] is not None else ''),'</td>', file=output)
            print('<td>',escapeHTML(r[3]),'</td>','</tr>', sep='', end='\n', file=output)
        print('</table>', sep='', end='', file=output)

        cur.close()

        if found is not None:
            print("<table>", file=output)
            print("<tr><td><h2>Репозиторий <a href='"+prefs.SITE_URL+"/s/download_repo/"+url[2]+"'>",branch,"</a></h2></td>", file=output)
            print('<td><h2>В СЭМД используются справочники</h2></tr>', file=output);
            print("<tr><td style='vertical-align:top'>", file=output)
            repopath = os.path.join(prefs.DATA_PATH, url[2])
            shortpath = ''
            for i in range(3, len(url)):
                repopath = os.path.join(repopath, url[i])
                shortpath = os.path.join(shortpath, url[i])
            all_files = os.listdir(repopath)
            files = [f for f in all_files if os.path.isfile(repopath+'/'+f)]
            dirs = [f for f in all_files if not os.path.isfile(repopath+'/'+f)]
            files.sort()
            dirs.sort()

            backpath = ""
            for i in range(3, len(url)-1):
                backpath = os.path.join(backpath, url[i])
            if backpath != "":
                backpath = '/'+backpath
            shortpath = '/'+shortpath+'/' if shortpath != "" else '/'
            currentpath = ""
            if len(url) > 3:
                currentpath = backpath+'/'+url[-1] if backpath != "" else url[-1]
            if currentpath != "":
                print("<div><a href='", prefs.SITE_URL, '/repo/',url[2],backpath,"'>&lArr; Назад</a>&nbsp;&nbsp;&nbsp;Текущий путь - '<strong>",currentpath,"</strong>'</div>", sep='', file=output)
            else:
                print("<div>&nbsp;</div>", sep='', file=output)
            print('<table class=\'bordered\'><tr><th>File</th><th>Size in bytes</th><th>Upload date</th></tr>', sep='', end='', file=output)
            for f in dirs:
                if f[0] != '.':
                    stat = os.stat(repopath+'/'+f)
                    print('<tr><td>/<a href="', prefs.SITE_URL, '/repo/',url[2],shortpath,slug(f),'">',f,'</a></td><td></td><td>',time.strftime("%d.%m.%Y %X", time.gmtime(stat.st_mtime)),'</td></tr>', sep='', file=output)
            for f in files:
                if f != 'Thumbs.db' and f != url[2]+'.zip':
                    stat = os.stat(repopath+'/'+f)
                    print('<tr><td>&nbsp;<a href="', prefs.SITE_URL, '/file/',url[2],shortpath,slug(f),'">',f,'</a></td><td align="right">','{0:,}'.format(stat.st_size).replace(',', ' '),'&nbsp;</td><td>',time.strftime("%d.%m.%Y %X", time.gmtime(stat.st_mtime)),'</td></tr>', sep='', file=output)
            print('</table>', sep='', end='', file=output)

            print('</td><td><table>', file=output);

            cur = conn.cursor()
            SQLPacket = '''select ref, version, ref_name
                from fnsi_semd_refs
                inner join fnsi_emd on fnsi_emd.git_link=fnsi_semd_refs.oid
                where fnsi_emd.id=?
                order by ref'''
            cur.execute(SQLPacket, (int(url[2]),))
            for r in cur.fetchall():
                found = True
                print('<tr><td><a href="', prefs.SITE_URL, '/ref/',r[0],'/',r[1],'">',r[0],'</a></td>', sep='', file=output)
                print('<td>',r[1],'</td>', file=output)
                print('<td>',r[2],'</td>','</tr>', sep='', end='\n', file=output)

            cur.close()
            conn.close()

            print('</table></td></tr></table></body></html>', sep='', end='', file=output)
        else:
            print('<h2>Репозиторий не скачан</h2>', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if len(url) >= 3 and url[1] == 'file' and url[2].isnumeric() and "\\.." not in environ['PATH_INFO']:
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select * from semd_spec_repo where type_remd=? limit 1'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        shortpath = "/".join(url[3:-1])
        f = None
        f_url = url[-1]
        if os.path.isdir(os.path.join(prefs.DATA_PATH, url[2], shortpath)):
            for f1 in os.listdir(os.path.join(prefs.DATA_PATH, url[2], shortpath)):
                if slug(f1) == f_url:
                    f = os.path.join(prefs.DATA_PATH, url[2], shortpath, f1)
                    break
        if f is None and os.path.isdir(os.path.join(prefs.VALIDATOR_DATA_PATH, url[2], shortpath)):
            for f1 in os.listdir(os.path.join(prefs.VALIDATOR_DATA_PATH, url[2], shortpath)):
                if f1 == f_url:
                    f = os.path.join(prefs.VALIDATOR_DATA_PATH, url[2], shortpath, f1)
                    break

        if rec is None or f is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        if f.endswith('.xsl') or f.endswith('.xsd') or f.endswith('.sch') or f.endswith('.xml'):
            response_headers = [('Content-type', 'text/xml')]
        elif f.endswith('.md') or f.endswith('.txt'):
            response_headers = [('Content-type', 'text/plain; charset=UTF-8')]
        elif f.endswith('.pdf'):
            response_headers = [('Content-type', 'application/pdf')]
        elif f.endswith('.doc') or f.endswith('.docx'):
            response_headers = [('Content-type', 'application/msword')]
        elif f.endswith('.png'):
            response_headers = [('Content-type', 'image/png')]
        elif f.endswith('.jpg'):
            response_headers = [('Content-type', 'image/jpg')]
        else:
            response_headers = [('Content-type', 'application/octet-stream')]

        status = '200 OK'
        start_response(status, response_headers)
        f = open(f, 'rb')

        if 'wsgi.file_wrapper' in environ:
            return environ['wsgi.file_wrapper'](f, block_size)
        else:
            return iter(lambda: filelike.read(block_size), '')


    if secret and len(url) == 4 and url[1] == 'checkout' and url[2].isnumeric():
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))

        cur = conn.cursor()
        cur.execute("select * from semd_spec_repo where TYPE_REMD=? and tag=? limit 1", (int(url[2]), url[3]))
        r = cur.fetchone()
        cur.close()

        if r is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        output = StringIO()
        if os.path.exists(os.path.join(prefs.DATA_PATH, url[2])):
            repo = Repo(os.path.join(prefs.DATA_PATH, url[2]))
            repo.git.checkout(url[3])

            cur = conn.cursor()
            cur.execute("select last_tag from repo_active_tag where type_remd=?", (int(url[2]),))
            last_tag = cur.fetchone()
            cur.close()

            user = environ['REMOTE_USER'] if last_tag[0] != url[3] and 'REMOTE_USER' in environ else None
            cur = conn.cursor()
            cur.execute("update repo_active_tag set active_tag=?, user=? where type_remd=?", (url[3], user, int(url[2])))
            cur.close()
            conn.commit()
            print('Выполнено.\nОбновите страницу с таблицей', file=output)
        else:
            print('Репозитория для типа ', url[2], ' не существует.\nЧто-то пошло не так', file=output)

        conn.close()

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]

    if environ['PATH_INFO'] == '/s/clear_active':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))

        output = StringIO()
        cur = conn.cursor()
        cur.execute("select type_remd, last_tag from repo_active_tag where active_tag<>last_tag")
        for r in cur.fetchall():
            if os.path.exists(os.path.join(prefs.DATA_PATH, str(r[0]))):
                repo = Repo(os.path.join(prefs.DATA_PATH, str(r[0])))
                repo.git.checkout(r[1])

                cur2 = conn.cursor()
                cur2.execute("update repo_active_tag set active_tag=last_tag, user=null where type_remd=?", (r[0],))
                cur2.close()            
                conn.commit()
            else:
                print('Репозитория для типа ', r[0], ' не существует.\nЧто-то пошло не так\n', file=output)
        cur.close()
        conn.close()

        print('Выполнено.\nОбновите страницу с таблицей', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if environ['PATH_INFO'] == '/s/download_all':
        fp_zip = Path(prefs.ZIP_NAME)
        path_to_archive = Path(prefs.DATA_PATH)
        with zipfile.ZipFile(path_to_archive.joinpath(fp_zip), "w", zipfile.ZIP_DEFLATED) as zipf:
            for fp in glob.glob(prefs.DATA_PATH+'/**/[!.]*', recursive=True):
                fp2 = Path(fp)
                if fp2.suffix not in ['.pdf', '.doc', '.docx', '.zip', '.db']:
                    zipf.write(fp, arcname=fp2.relative_to(path_to_archive))

        status = '200 OK'
        response_headers = [('Content-type', 'application/zip')]
        start_response(status, response_headers)
        f = open(path_to_archive.joinpath(fp_zip), 'rb')

        if 'wsgi.file_wrapper' in environ:
            return environ['wsgi.file_wrapper'](f, block_size)
        else:
            return iter(lambda: filelike.read(block_size), '')


    if secret and len(url) == 3 and url[1] == 'download_repo' and url[2].isnumeric():
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select * from semd_spec_repo where type_remd=? limit 1'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        if rec is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        fp_zip = Path(url[2]+'.zip')
        path_to_archive = Path(prefs.DATA_PATH+'/'+url[2])
        with zipfile.ZipFile(path_to_archive.joinpath(fp_zip), "w", zipfile.ZIP_DEFLATED) as zipf:
            for fp in path_to_archive.glob('**/*'):
                if fp.name != fp_zip.name:
                    zipf.write(fp, arcname=fp.relative_to(path_to_archive))

        status = '200 OK'
        response_headers = [('Content-type', 'application/zip')]
        start_response(status, response_headers)
        f = open(path_to_archive.joinpath(fp_zip), 'rb')

        if 'wsgi.file_wrapper' in environ:
            return environ['wsgi.file_wrapper'](f, block_size)
        else:
            return iter(lambda: filelike.read(block_size), '')


    if environ['PATH_INFO'] == '/s/check_validator_sch':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        output = StringIO()

        path_to_archive = Path(prefs.DATA_PATH)
        path_to_validator_archive = Path(prefs.VALIDATOR_DATA_PATH)
        sch_dict = {}
        xsl_dict = {}
        for fp in sorted(path_to_validator_archive.glob('*.sch')):
            type_remd_str = fp.stem.split('_', maxsplit=1)[0]
            type_remd = int(type_remd_str)
            xsl = fp.parents[0].joinpath('schematrons').joinpath(type_remd_str+'.xsl')
            xsl_flag = xsl.exists()
            if type_remd not in sch_dict:
                sch_dict[type_remd] = [fp]
                xsl_dict[type_remd] = [xsl_flag]
            else:
                sch_dict[type_remd].append(fp)
                xsl_dict[type_remd].append(xsl_flag)

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Проверка актуальности базы SCH валидатора СЭМД</title>
</head>
<body>
<H2>Проверка актуальности базы SCH валидатора СЭМД</H2>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p>
<table class="bordered selected">
<tr><th>Тип РЭМД</th><th>СЭМД</th><th>Файлы валидатора</th><th>Файлы репозитория</th><th>Совпало/Проверить</th></tr>''', sep='', end='\n', file=output);

        cur = conn.cursor()
        SQLPacket = '''select fnsi_emd.id, fnsi_emd.name, END_DATE, repo_active_tag.active_tag, repo_active_tag.user
            from fnsi_semd_spec
            inner join fnsi_emd on fnsi_emd.git_link=fnsi_semd_spec.OID
            left join repo_active_tag on repo_active_tag.type_remd=fnsi_emd.id and active_tag<>last_tag
            order by fnsi_emd.id'''
        cur.execute(SQLPacket)
        for r in cur.fetchall():
            delta_end = datetime.datetime.now()-datetime.datetime.strptime(r[2], '%d.%m.%Y') if r[2] is not None else datetime.timedelta(days = -1)
            repo = "<a href='" + prefs.SITE_URL + '/repo/' + str(r[0]) + "'>" + r[1] + "</a>"
            repo_active_tag = '<br><small><strong>'+r[3]+'</strong>'+ (' Пользователь - '+r[4] if r[4] is not None else '')+'</small>' if r[3] is not None else ''
            tr_repo_color = ""
            td2_repo_color = ""
            td1_tds_color = ""
            td1_tds = ""
            if delta_end.days>0:
                tr_repo_color = " style='background-color:#cccccc'"
            elif r[3] is not None:
                td2_repo_color = " style='background-color:#FF5050'"
            elif str(r[0]) in tds_templates:
                td1_tds_color = " style='background-color:#A0FFFF'"
                td1_tds = " Ш"
            elif r[0] in prefs.TYPE_REMD_IN_CONFIG:
                td1_tds_color = " style='background-color:#A0FFFF'"
                td1_tds = " К"
            print('<tr',tr_repo_color,'><td align="center"',td1_tds_color,'>',str(r[0]),td1_tds,'</td>', sep='', end='', file=output)
            print("<td",td2_repo_color,'>',repo,repo_active_tag,'</td>', sep='', end='', file=output)
            repopath = Path(os.path.join(prefs.DATA_PATH, str(r[0])))
            if repopath.exists():
                sch_sizes = []
                sch_names = []
                len_sch_names = 0
                if r[0] in sch_dict:
                    for sch in sch_dict[r[0]]:
                        sch_sizes.append(sch.stat().st_size)
                        sch_names.append(sch)
                    len_sch_names = len(sch_names)

                sch_count = 0
                td1 = ""
                td2 = ""
                sch_repo = sorted(repopath.glob('**/*.sch'))
                for fp in sch_repo:
                    size = fp.stat().st_size
                    found = False
                    for i in reversed(range(len(sch_sizes))):
                        if sch_sizes[i] == size and compareFiles(fp, sch_dict[r[0]][i]):
                            found = True
                            sch_count += 1
                            break

                    fp2 = fp.relative_to(path_to_archive)
                    fp2_str = str(fp2).split('/', maxsplit=1)[1]
                    if found:
                        td1 += "<div style='font-weight: bold'>" + sch_names[i].name + "</div>"
                        td2 += "<div style='font-weight: bold'>" + fp2_str + "</div>" 
                        del sch_sizes[i]
                        del sch_names[i]
                    else:
                        td2 += "<div><span style='background-color:#FFC0C0'><a href='" + prefs.SITE_URL + '/s/sch_to_validator/' + str(r[0]) + '/' + slug(fp2_str) + "'  target='_blank' onclick='return confirm(\"Заменить в валидаторе у типа "+str(r[0])+" схематрон на ["+fp2_str+"] из репозитория?\")'>" + fp2_str + "</a></span></div>" 

                for sch in sch_names:
                    td1 += "<div><span style='background-color:#FFC0C0'>"+sch.name+'</span></div>'

                print('<td>', td1, '<span style="font-size: 150%; font-weight: bold">(НЕТ XSL)</span>' if td1 != "" and not xsl_dict[r[0]] else '', '</td>', sep='', end='', file=output)
                print('<td>', td2,'</td>', sep='', end='', file=output)
                if len(sch_repo) == 0:
                    print('<td></td></tr>', sep='', file=output)
                else:
                    print("<td", td1_tds_color, " align='center'>", 
                        "<a href='" + prefs.SITE_URL + "/s/validate_semd/" + str(r[0]) + "'>ДА</a>" if sch_count==len_sch_names and len_sch_names!=0 else ("Нет" if td1_tds_color=="" else "<span style='font-weight: bold; color: red;'>ОБНОВИТЕ!</span>"), 
                        '</td>','</tr>', sep='', file=output)
            else:
                print('<td>Репозиторий не найден</td></tr>', file=output)

        print('</table></body></html>', sep='', end='', file=output)

        conn.close()

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if environ['PATH_INFO'] == '/s/check_validator_xsd':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        output = StringIO()

        path_to_archive = Path(prefs.DATA_PATH)
        path_to_validator_archive = Path(prefs.VALIDATOR_DATA_PATH)
        xsd_dict = {}
        for fp in sorted(path_to_validator_archive.glob('**/*.xsd')):
            fp2 = fp.relative_to(path_to_validator_archive)
            try:
                type_remd = int(str(fp2).split('/', maxsplit=1)[0])
                if type_remd not in xsd_dict:
                    xsd_dict[type_remd] = [fp]
                else:
                    xsd_dict[type_remd].append(fp)
            except ValueError as e:
                pass

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Проверка актуальности базы XSL валидатора СЭМД</title>
</head>
<body>
<H2>Проверка актуальности базы XSD валидатора СЭМД</H2>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p>
<table class='bordered'>
<tr><th>Тип РЭМД</th><th>СЭМД</th><th>Файлы валидатора</th><th>Файлы репозитория</th><th>Совпало/Проверить</th></tr>''', sep='', end='\n', file=output);

        cur = conn.cursor()
        SQLPacket = '''select fnsi_emd.id, fnsi_emd.name, END_DATE, repo_active_tag.active_tag, repo_active_tag.user
            from fnsi_semd_spec
            inner join fnsi_emd on fnsi_emd.git_link=fnsi_semd_spec.OID
            left join repo_active_tag on repo_active_tag.type_remd=fnsi_emd.id and active_tag<>last_tag
            order by fnsi_emd.id'''
        cur.execute(SQLPacket)
        for r in cur.fetchall():
            delta_end = datetime.datetime.now()-datetime.datetime.strptime(r[2], '%d.%m.%Y') if r[2] is not None else datetime.timedelta(days = -1)
            repo = "<a href='" + prefs.SITE_URL + '/repo/' + str(r[0]) + "'>" + r[1] + "</a>"
            repo_active_tag = '<br><small><strong>'+r[3]+'</strong>'+ (' Пользователь - '+r[4] if r[4] is not None else '')+'</small>' if r[3] is not None else ''
            tr_repo_color = ""
            td2_repo_color = ""
            td1_tds_color = ""
            td1_tds = ""
            if delta_end.days>0:
                tr_repo_color = " style='background-color:#cccccc'"
            elif r[3] is not None:
                td2_repo_color = " style='background-color:#FF5050'"
            elif str(r[0]) in tds_templates:
                td1_tds_color = " style='background-color:#A0FFFF'"
                td1_tds = " Ш"
            elif r[0] in prefs.TYPE_REMD_IN_CONFIG:
                td1_tds_color = " style='background-color:#A0FFFF'"
                td1_tds = " К"
            print('<tr',tr_repo_color,'><td align="center"',td1_tds_color,'>',str(r[0]),td1_tds,'</td>', sep='', end='', file=output)
            print("<td",td2_repo_color,'>',repo,repo_active_tag,'</td>', sep='', end='', file=output)
            repopath = Path(os.path.join(prefs.DATA_PATH, str(r[0])))
            if repopath.exists():
                xsd_sizes = []
                xsd_names = []
                if r[0] in xsd_dict:
                    for xsd in xsd_dict[r[0]]:
                        xsd_sizes.append(xsd.stat().st_size)
                        xsd_names.append(xsd)

                td1 = ""
                td2 = ""
                idx_found = set()
                for fp in sorted(repopath.glob('**/*.xsd')):
                    size = fp.stat().st_size
                    found = False
                    for i in range(len(xsd_sizes)):
                        if xsd_sizes[i] == size and compareFiles(fp, xsd_dict[r[0]][i]):
                            found = True
                            idx_found.add(i)
                            fp2 = fp.relative_to(path_to_archive)
                            fp2_str = str(fp2).split('/', maxsplit=1)[1]
                            if fp2.name == "CDA.xsd":
                                td2 += "<div style='font-weight: bold'><a href='" + prefs.SITE_URL + '/s/xsd_to_validator/' + str(r[0]) + '/' + slug(fp2_str) + "' target='_blank' onclick='return confirm(\"Заменить в валидаторе у типа "+str(r[0])+" схему на ["+fp2_str+"] из репозитория?\")'>" + fp2_str + "</a></div>" 
                            else:
                                td2 += "<div style='font-weight: bold'>"+fp2_str+"</div>" 
                    if not found:
                        fp2 = fp.relative_to(path_to_archive)
                        fp2_str = str(fp2).split('/', maxsplit=1)[1]
                        if fp2.name == "CDA.xsd":
                            td2 += "<div><span style='background-color:#FFC0C0'><a href='" + prefs.SITE_URL + '/s/xsd_to_validator/' + str(r[0]) + '/' + slug(fp2_str) + "' target='_blank' onclick='return confirm(\"Заменить в валидаторе у типа "+str(r[0])+" схему на ["+fp2_str+"] из репозитория?\")'>" + fp2_str + "</a></span></div>" 
                        else:
                            td2 += "<div><span style='background-color:#FFC0C0'>"+fp2_str+"</span></div>" 

                if len(idx_found) > 0:
                    for i in sorted(idx_found):
                        fp1 = xsd_names[i].relative_to(path_to_validator_archive)
                        fp1_str = str(fp1).split('/', maxsplit=1)[1]
                        td1 += "<div style='font-weight: bold'>"+fp1_str+"</div>"
                        xsd_names[i] = None

                valid = True
                for xsd in xsd_names:
                    if xsd is not None:
                        fp1 = xsd.relative_to(path_to_validator_archive)
                        fp1_str = str(fp1).split('/', maxsplit=1)[1]
                        td1 += "<div style='background-color:#FFC0C0'>"+fp1_str+'</div>'
                        valid = False

                print('<td>', td1,'</td>', sep='', end='', file=output)
                print('<td>', td2,'</td>', sep='', end='', file=output)
                if len(tr_repo_color) > 0:
                    print('<td></td></tr>', sep='', end='', file=output)
                else:
                    print("<td", td1_tds_color, " align='center'>", 
                        "<a href='" + prefs.SITE_URL + "/s/validate_semd/" + str(r[0]) + "'>ДА</a>" if valid and len(xsd_names)!=0 else ("Нет" if td1_tds_color=="" else "<span style='font-weight: bold; color: red;'>ОБНОВИТЕ!</span>"), 
                        '</td>','</tr>', sep='', file=output)
            else:
                print('<td>Репозиторий не найден</td></tr>', file=output)

        print('</table></body></html>', sep='', end='', file=output)

        conn.close()

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if secret and len(url) >= 4 and url[1] == 'sch_to_validator' and url[2].isnumeric() and "\\.." not in environ['PATH_INFO']:
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select * from semd_spec_repo where type_remd=? limit 1'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        output = StringIO()

        fullpath = Path(prefs.DATA_PATH, url[2])
        f = None
        f_url = "/".join(url[3:])
        for f1 in fullpath.glob("**/*.sch"):
            f2 = str(f1.relative_to(fullpath))
            if slug(f2) == f_url:
                f = f1
                break

        if rec is None or f is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        shutil.copyfile(f, os.path.join(prefs.VALIDATOR_DATA_PATH, url[2]+".sch"))

        r = requests.get(prefs.VALIDATOR_URL+'/updatedb/'+url[2],
            headers = {'Accept': 'text/plain'}
        )
        print("<!DOCTYPE html><html><head><meta charset='utf-8'></head><body>", sep='', end='\n', file=output)
        print(str(r.status_code) + " " + r.text, file=output)
        print('''<br><iframe src="'''+prefs.SITE_URL+"/s/validate_semd/"+url[2]+'''" width="100%" height="100%" frameborder="0"> </iframe>''', sep='', end='\n', file=output)
        print("</body></html>", sep='', end='\n', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if secret and len(url) >= 4 and url[1] == 'xsd_to_validator' and url[2].isnumeric() and "\\.." not in environ['PATH_INFO']:
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select * from semd_spec_repo where type_remd=? limit 1'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        fullpath = Path(prefs.DATA_PATH, url[2])
        f = None
        f_url = "/".join(url[3:])
        for f1 in fullpath.glob("**/CDA.xsd"):
            f2 = str(f1.relative_to(fullpath))
            if slug(f2) == f_url:
                f = f1
                break

        if rec is None or f is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        dst = Path(prefs.VALIDATOR_DATA_PATH, url[2])
        src = f.parents[0]
        shutil.rmtree(dst, ignore_errors=True)

        # copy_directory(src, dst):
        src2 = []
        dst2 = []
        dirs2 = set()
        for fp in src.glob('**/[!.]*'):
            if fp.suffix == '.xsd':
                src2.append(fp)
                fp2 = Path(fp).relative_to(src)
                fp3 = dst.joinpath(fp2)
                dst2.append(fp3)
                dirs2.add(fp3.parents[0])

        for dir in sorted(dirs2):
            dir.mkdir()

        for i in range(len(src2)):
            shutil.copy(src2[i], dst2[i])

        output = StringIO()
        print("<!DOCTYPE html><html><head><meta charset='utf-8'></head><body>", sep='', end='\n', file=output)
        print("OK", file=output)
        print('''<br><iframe src="'''+prefs.SITE_URL+"/s/validate_semd/"+url[2]+'''" width="100%" height="100%" frameborder="0"> </iframe>''', sep='', end='\n', file=output)
        print("</body></html>", sep='', end='\n', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if environ['PATH_INFO'] == '/refs':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        output = StringIO()

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Справочники ФНСИ</title>
</head>
<body>
<H1>Справочники ФНСИ</H1>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p><p>\n
<table class="selected bordered" style='width: 100%; table-layout : fixed;' border=1>
<tr><th width="10%">OID</th><th width="5%">Версия (актуальность)</th><th>Название</th><th>Типы РЭМД</th><th width="3%">Число строк</th></tr>''', sep='', end='\n', file=output);

        cur = conn.cursor()
        SQLPacket = '''select fnsi_ref.codeSystem, fnsi_ref.version, fnsi_ref.codeSystemName, fnsi_ref.rowsCount, fnsi_ref.actual, ref_setting.load
            from fnsi_ref
            left join ref_setting on ref_setting.codeSystem=fnsi_ref.codeSystem
            order by fnsi_ref.codeSystem'''
        cur.execute(SQLPacket)

        count = 0
        for r in cur.fetchall():
            td1_bg_color = ""
            if r[5] == 0:
                td1_bg_color = " style='background:#cccccc'"
            else:
                count += r[3]
                 
            print('<tr><td',td1_bg_color,'>',r[0],'</td><td><a href="', prefs.SITE_URL, '/ref/',r[0],'/',r[1],'">',r[1],'</a>',' ('+r[4]+')' if r[4] is not None else '',
                '</td><td>',r[2],'</td><td>', sep='', end='', file=output)
            remd = ''
            try:
                for a in fnsi_emd[r[0]+' '+r[1]]:
                    type_remd = a['ID']
                    type_remd_s = str(type_remd)
                    bg_color = ""
                    if a["END_DATE"] is not None:
                        bg_color = " style='background:#cccccc'"
                    elif type_remd_s in tds_templates or type_remd in prefs.TYPE_REMD_IN_CONFIG:
                        bg_color = " style='background:#A0FFFF'"
                    remd += '<a href="'+ prefs.SITE_URL+ '/repo/'+type_remd_s+'" title="'+a['NAME']+'"><span'+bg_color+'>'+str(a['ID'])+"</span></a> "
            except KeyError as e:
                pass
            print(remd, sep='', end='', file=output)
            print('</td><td',td1_bg_color,' align="right">',r[3],'</td></tr>', sep='', file=output)

        cur.close()
        conn.close()
        print('</table>', sep='', end='', file=output)
        print('<p>Всего строк - ', '{0:,}'.format(count).replace(',', ' '), '</p>', sep='', end='', file=output)
        print('</body></html>', sep='', end='', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if len(url) == 4 and url[1] == 'ref':
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        cur.execute('''select 
            fnsi_ref.codeSystem, 
            fnsi_ref.version,
            fnsi_ref.codeSystemName,
            fnsi_ref.rowsCount,
            fnsi_ref.codeSystemOther,
            fnsi_ref.keyField,
            fnsi_ref.valueField,
            fnsi_ref.fieldNames,
            fnsi_ref.actual,
            ref_setting.load,
            ref_setting.validateRefName,
            ref_setting.keyField,
            ref_setting.valueField,
            ref_setting.validateValue,
            ref_setting.rekv,
            ref_setting.comment
            from fnsi_ref 
            left join ref_setting on fnsi_ref.codeSystem=ref_setting.codeSystem
            where fnsi_ref.codeSystem=? and (fnsi_ref.version=? or fnsi_ref.actual=?)''', (url[2], url[3], url[3]))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        output = StringIO()
        if rec is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Просмотр справочника</title>
</head>
<body>
<H2>Просмотр справочника ''',rec[0],' (',rec[2],')','''</H2>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p><p>\n
<a href="https://nsi.rosminzdrav.ru/port/rest/passport?userKey=''',prefs.FNSI_userkey,'&identifier=',rec[0],'&version=',rec[1],'''">Паспорт</a><br>
<a href="https://nsi.rosminzdrav.ru/port/rest/data?userKey=''',prefs.FNSI_userkey,'&identifier=',rec[0],'&version=',rec[1],'''&page=1&size=50">Данные (первые 50 записей)</a><br>
<a href="https://nsi.rosminzdrav.ru/port/rest/versions?userKey=''',prefs.FNSI_userkey,'&identifier=',rec[0],'''">Все версии</a></p>
<table class="bordered" border=1>
<tr><th>Поле</th><th>Значение</th><th>По настройкам валидатора</th></tr>''', sep='', end='\n', file=output);

        print('<tr><td>Загружать</td><td></td><td>',rec[9],'</td></tr>', sep='', file=output)
        print('<tr><td>Выполнять проверку<br>codeSystemName в СЭМД</td><td></td><td>',rec[10],'</td></tr>', sep='', file=output)
        print('<tr><td>version</td><td>',rec[1],'</td><td></td></tr>', sep='', file=output)
        print('<tr><td>Число строк</td><td>','{0:,}'.format(rec[3]).replace(',', ' '),'</td><td></td></tr>', sep='', file=output)
        print('<tr><td>codeSystemOther</td><td>',rec[4],'</td><td></td></tr>', sep='', file=output)
        print('<tr><td>Ключ</td><td>',rec[5],'</td><td>',rec[11] if rec[12] is not None else '','</td></tr>', sep='', file=output)
        print('<tr><td>Значение</td><td>',rec[6],'</td><td>',' <b>НЕ ПРОВЕРЯТЬ displayName</b>' if rec[13]==0 else (rec[12] if rec[12] is not None else ''),'</td></tr>', sep='', file=output)
        print('<tr><td>Имена полей</td><td>',rec[7],'</td><td>',rec[14],'</td></tr>', sep='', file=output)
        print('<tr><td>Признак актуальности (*)</td><td>',rec[8],'</td><td></td></tr>', sep='', file=output)
        print('<tr><td>Комментарий</td><td></td><td>',rec[15] if rec[15] is not None else '','</td></tr>', sep='', file=output)
        print('</table>', sep='', end='', file=output)

        print('<h2>Содержимое</h2>', sep='', end='', file=output)
        print("<p><a href='" + prefs.SITE_URL+"/data?userKey=mzrepos&identifier="+rec[0] , '&version=', rec[1], "'>Сервис - валидатор</a></p>", sep='', end='\n', file=output);
        print('</body></html>', sep='', end='', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]



    if environ['PATH_INFO'] == '/s/check_tds_vis':
        conn = sqlite3.connect(os.path.join(tds_prefs.DATA_PATH, tds_prefs.DB_NAME))
        output = StringIO()
        path_to_archive = Path(prefs.DATA_PATH)

        print('''<!DOCTYPE html><html><head>
<meta charset='utf-8'>
<link rel='stylesheet' href="''', prefs.SITE_URL, '''/style.css"><title>Проверка актуальности визуализаторов в сервисе распространения ШМД</title>
</head>
<body>
<H2>Проверка актуальности визуализаторов в сервисе распространения ШМД</H2>
<p><a href="''',prefs.SITE_URL,'''/">В начало</a></p>
<table class="selected bordered" style='width: 100%; table-layout : fixed;' border=1>
<tr><th width="3%">Тип РЭМД</th><th width="50%">СЭМД</th><th>Файл сервиса</th><th>Файлы репозитория</th><th width="3%">Актуально</th></tr>''', sep='', end='\n', file=output);

        cur = conn.cursor()
        SQLPacket = "select typeREMDCode, fileName, dateUploaded from visualizer order by CAST(typeREMDCode as INTEGER)"
        cur.execute(SQLPacket)
        for r in cur.fetchall():
            dateUploaded = datetime.datetime.fromisoformat(r[2])
            repo = "<a href='" + prefs.SITE_URL + '/repo/' + str(r[0]) + "'>" + fnsi_emd2[r[0]] + "</a>"
            print('<tr><td align="center">',str(r[0]),'</td>', sep='', end='', file=output)
            print('<td>',repo,'</td>', sep='', end='', file=output)
            repopath = Path(os.path.join(prefs.DATA_PATH, str(r[0])))
            if repopath.exists():
                td1 = ""
                td2 = ""
                foundT = False
                foundN = False
                xsl_repo = sorted(repopath.glob('**/*.xsl'))
                for fp in xsl_repo:
                    t = fp.stat().st_mtime
                    modified = datetime.datetime.fromtimestamp(t)
                    fp2 = fp.relative_to(path_to_archive)
                    fp2_str = str(fp2).split('/')[-1]
                    if dateUploaded > modified:
                        foundT = True
                    if r[1] == fp2_str:
                        foundN = True

                    if foundT:
                        td2 += "<div " + ("style='font-weight: bold'" if r[1] == fp2_str else "") + ">" + fp2_str + "</div><div style='margin-bottom:5px'><small>" + modified.strftime("%d.%m.%Y %H:%M:%S") + "</small></div>"
                    else:
                        td2 += "<div><span>" + fp2_str + "</span></div><div style='margin-bottom:5px'><span style='background-color:#FFC0C0'><small>" + modified.strftime("%d.%m.%Y %H:%M:%S") + "</small></span></div>"

                if foundT:
                    td1 += "<div " + ("style='font-weight: bold'" if foundN else "")+">" + r[1] + "</div><div><small>" + dateUploaded.strftime("%d.%m.%Y %H:%M:%S") + "</small></div>"
                else:
                    td1 += "<div><span>" + r[1] + "</span></div><div><span style='background-color:#FFC0C0'><small>" + dateUploaded.strftime("%d.%m.%Y %H:%M:%S") + "</small></span></div>"

                print('<td>', td1, '</td>', '<td>', td2, '</td>', sep='', end='', file=output)
                print('<td>', '<b>ДА</b>' if foundT else 'Нет', '</td>','</tr>', sep='', file=output)
            else:
                print('<td>Репозиторий не найден</td></tr>', file=output)

        print('</table></body></html>', sep='', end='', file=output)

        conn.close()

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if environ['PATH_INFO'] == '/validator_settings':
        status = '200 OK'
        response_headers = [('Content-type', 'application/json,')]
        start_response(status, response_headers)
        f = open(os.path.join(prefs.VALIDATOR_DATA_PATH, 'FNSIlist.json'))

        if 'wsgi.file_wrapper' in environ:
            return environ['wsgi.file_wrapper'](f, block_size)
        else:
            return iter(lambda: filelike.read(block_size), '')


    if secret and len(url) == 2 and url[1] == 'send_semd.html':
        output = StringIO()

        print("<!DOCTYPE html><html>", "<head>", "<link rel='stylesheet' href=", prefs.SITE_URL, "/style.css>", "  <title>Валидация СЭМД</title>", "</head>", "<body>",
                "<H1>Валидация СЭМД</H1>",
                "<form enctype = 'multipart/form-data' method='post' action='", prefs.SITE_URL, "/s/verify'>",
                "  <fieldset>", "   <p><label for='remdtype'><a href='", prefs.SITE_URL+"/",
                "'>Тип РЭМД (OID)</a>:</label><input type='text' name='remdtype'></p>",
                "   <p><label for='verifytype'>Тип валидации. 0 - xsd, 1 - sch, 2 - xsd+sch, 3 - xsd+sch+fnsi :</label><input type='text' name='verifytype' value='3'></p>",
                "  </fieldset>", "  <p>", "   <input type='file' name='file'>",
                "   <input type='submit' value='Отправить СЭМД'>", "  </p>", "</form>", "<hr>", "<h2>См. также</h2>",
                "<a href='", prefs.SITE_URL+ "/validator_settings", "'>Настройки валидатора справочников ФНСИ (просмотр)</a><br>",
                "<a href='", prefs.SITE_URL+ "/", "'>Домой</a>",
                "</body>", "</html>", sep='', end='', file=output);

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if secret and len(url) == 3 and url[1] == 'validate_semd' and url[2].isnumeric():
        conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))
        cur = conn.cursor()
        SQLPacket = '''select * from semd_spec_repo where type_remd=? limit 1'''
        cur.execute(SQLPacket, (int(url[2]),))
        rec = cur.fetchone()
        cur.close()
        conn.close()

        if rec is None:
            start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
            return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']

        fp = os.path.join(prefs.VALIDATOR_DATA_PATH, url[2], "semds")

        SQLPacket = f'''select max(dateUploaded)
            from template 
            inner join templateType on template.UUIDTemplate=templateType.UUIDTemplate
            where configName=? and configVersion=? and typeREMDCode=? '''

        conn = sqlite3.connect(os.path.join(tds_prefs.DATA_PATH, tds_prefs.DB_NAME))
        cursor = conn.cursor()
        cursor.execute(SQLPacket, (conf,cv, url[2]))
        rec = cursor.fetchone()
        conn.close()

        output = StringIO()
        print("<!DOCTYPE html><html><head>", "<link rel='stylesheet' href=", prefs.SITE_URL, "/style.css>", "  <title>Валидация СЭМД</title>", "</head>", "<body>",
        "<H2>Проверка исторических СЭМД в сервисе валидации для типа РЭМД - ", url[2], "</H2>",
        '''<table class="bordered" style='width: 100%; table-layout : fixed;' border=1>
<tr><th width='20%'>СЭМД (имя - хеш)</th><th width='10%'>Дата загрузки</th><th width='5%'>Размер</th><th>Результат проверки</th></tr>''', sep='', end='\n', file=output)
        semdpath = Path(fp)
        if semdpath.exists():
            dateUploaded = None
            if rec is not None and rec[0] is not None:
                dateUploaded = datetime.datetime.strptime(rec[0], "%Y-%m-%dT%H:%M:%S.%f")

            # второй проход. Проверяем оставшиеся СЭМДы
            semds = semdpath.glob('*.xml')
            for ff in semds:
                t = ff.stat().st_mtime
                modified = datetime.datetime.fromtimestamp(t)
                f = {'file': (ff.name, open(ff, 'rb'), 'application/xml')}
                d = {'remdtype': url[2], 'verifytype': '2'}
                response = requests.post(prefs.VALIDATOR_URL + "/verify", files=f, data=d)

                valid = (response.status_code == 200 and "<result>" in response.text and ":ERROR: " not in response.text and ":failed-assert " not in response.text)
                print("<tr><td><a href='", prefs.SITE_URL, '/file/',url[2],"/semds/",ff.name,"'>", ff.name, "</a><br>", "<small><a href="+ prefs.SITE_URL+ "/s/removeSemd/"+url[2]+"/"+ff.name+">Удалить</a></small></td>", 
                    "<td>", modified.strftime("%Y-%m-%d %H:%M:%S"), " <b>Старее ШМД</b>" if dateUploaded is not None and dateUploaded > modified else "", "</td>", 
                    "<td>", ff.stat().st_size, "</td><td>",
                    "OK" if valid else ("response.status_code="+str(response.status_code) if response.status_code != 200 else "<details><summary>ОШИБКИ</summary><xmp>"+response.text+"</xmp></details>"),
                    "</td></tr>", sep='', end='\n', file=output)

        print("</table></body></html>", sep='', end='\n', file=output)

        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    if secret and len(url) == 2 and url[1] == 'verify':
        valid = False
        fn = None
        try:
            length = int(environ.get('CONTENT_LENGTH', 0))
            if length > 5000000:   # 5 mb limit
                start_response('200 OK', [('Content-Type','text/html; charset=utf-8')])
                return [b'<p>File is too large - ' + str(environ.get('CONTENT_LENGTH', 0)).encode('UTF-8') + b'</p>']
            body = read(environ, length)
            form = cgi.FieldStorage(fp=body, environ=environ, keep_blank_values=True)
            fileitem = form['file']
            remdtype = form['remdtype'].value
            verifytype = form['verifytype'].value
            if not remdtype.isdigit() or not os.path.exists(os.path.join(prefs.VALIDATOR_DATA_PATH, remdtype)):
                start_response('200 OK', [('Content-Type','text/html; charset=utf-8')])
                return [b"<p>Wrong remdtype - '" + remdtype.encode('UTF-8') + b"'</p>"]
            if verifytype not in ('0', '1', '2', '3'):
                start_response('200 OK', [('Content-Type','text/html; charset=utf-8')])
                return [b"<p>Wrong verifytype - '" + verifytype.encode('UTF-8') + b"'</p>"]
            d = fileitem.file.read()
            fp = os.path.join(prefs.VALIDATOR_DATA_PATH, remdtype, "semds")
            fn = os.path.join(fp, str(uuid.uuid4()) + ".xml")
            if not os.path.exists(fp):
                os.makedirs(fp)
            sw = open(fn, 'wb')
            sw.write(d)
            sw.close()

            f = {'file': (fileitem.filename, open(fn, 'rb'), 'application/xml')}
            d = {'remdtype': remdtype, 'verifytype': verifytype}
            response = requests.post(prefs.VALIDATOR_URL + "/verify", files=f, data=d)

            valid = (response.status_code == 200 and "<result>" in response.text and ":ERROR: " not in response.text and ":failed-assert " not in response.text)
        finally:
            if fn is not None:
                if not valid or verifytype not in ('2', '3'):       # оставляем только валидные СЭМД
                    os.remove(fn)
                else:
                    # контроль, что такого файла в папке нет. В имени файла записывает хеш файла
                    # и контроль идет по именам файлов. Контроль на число СЭМД в хранилище, старые 
                    # замещаются новыми
                    file_md5 = filehash(fn)+".xml"
                    removed = False
                    semds_count = 1
                    min_time = None
                    min_name = None
                    for ff in Path(fp).glob('*.xml'):
                        if os.path.join(fp, ff.name) != fn:
                            t = ff.stat().st_mtime
                            modified = datetime.datetime.fromtimestamp(t)
                            if min_time is None or min_time > modified:
                                min_time = modified
                                min_name = ff.name
                            if file_md5 == ff.name:
                                os.remove(fn)
                                removed = True
                                break
                            semds_count += 1
                    if not removed:
                        if semds_count > prefs.SEMDS_IN_STORE:
                            os.remove(os.path.join(fp, min_name))
                        os.rename(fn, os.path.join(fp, file_md5))

        output = StringIO()
        print(response.text, file=output)
        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/plain; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    # редирект в jetty
    if len(url) == 2 and url[1] in ('data'):
        if len(environ["QUERY_STRING"]) > 0 :
            response = requests.get(prefs.VALIDATOR_URL + "/" + url[1] + "?" + environ["QUERY_STRING"])
        else:
            response = requests.get(prefs.VALIDATOR_URL + "/" + url[1])
        start_response(str(response.status_code) + ' ' + response.reason, [
            ('Content-Type', response.headers['Content-Type']),
            ('Content-Length', str(len(response.content)))
        ])
        return [response.content]


    if secret and len(url) == 4 and url[1] == 'removeSemd' and url[2].isnumeric() and "\\.." not in environ['PATH_INFO']:
        fn = Path(prefs.VALIDATOR_DATA_PATH, url[2], 'semds', url[3])
        os.remove(fn)

        output = StringIO()
        print("Файл ", url[3], " удален", file=output)
        ret = output.getvalue().encode('UTF-8')
        start_response('200 OK', [
            ('Content-Type', 'text/html; charset=utf-8'),
            ('Content-Length', str(len(ret)))
        ])
        return [ret]


    start_response('404 Not Found', [('Content-Type','text/html; charset=utf-8')])
    return [b'<p>Page Not Found</p>'+environ['PATH_INFO'].encode('UTF-8')+b'\n']


def main() -> int :
    error_file = StringIO()
    conn = sqlite3.connect(os.path.join(prefs.DATA_PATH, prefs.DB_NAME))

    r1 = loadFNSIref_semd_spec(conn, error_file)
    err = error_file.getvalue()
    print(err if len(err)>0 else r1)

    r2 = loadFNSIref_emd_notification(conn, error_file)
    err = error_file.getvalue()
    print(err if len(err)>0 else r2)

    cur = conn.cursor()
    cur.execute("select id from fnsi_emd order by id")
    for r in cur.fetchall():
        type_remd = str(r[0])
        print("Загрузка репозитория для типа РЭМД -", type_remd)
        updaterepo_func(conn, type_remd)
        
    cur.close()

    cur = conn.cursor()
    cur.execute("update fnsi_ref_ver set version=strftime('%d.%m.%Y','now') where oid='repos'")
    cur.close()
    conn.commit()

    conn.close()

    path_to_archive = Path(prefs.DATA_PATH)
    try:
        os.remove(os.path.join(prefs.DATA_PATH, prefs.ZIP_NAME))
    except OSError as e:
        pass


    return 1

if __name__ == '__main__' :
    sys.exit(main())
